package cn.angis.system.controller;

import cn.angis.common.annotation.SLog;
import cn.angis.common.api.ApiErrorCode;
import cn.angis.common.constant.Constant;
import cn.angis.common.constant.Crypto;
import cn.angis.common.enums.DeviceType;
import cn.angis.common.model.R;
import cn.angis.db.controller.BaseController;
import cn.angis.system.dto.input.LoginInput;
import cn.angis.system.dto.output.LoginOutput;
import cn.angis.system.dtomapper.UserStruct;
import cn.angis.system.model.AngisUser;
import cn.angis.system.model.Role;
import cn.angis.system.service.AngisUserService;
import cn.angis.system.service.DeptService;
import cn.angis.system.service.RoleService;
import cn.angis.system.service.RoleresService;
import cn.dev33.satoken.SaManager;
import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.temp.SaTempUtil;
import cn.dev33.satoken.util.SaResult;
import cn.openjava.gmsm.sm2.SM2;
import cn.openjava.gmsm.sm3.SM3;
import cn.wmj.util.UserAgent;
import com.jthinking.common.util.ip.IPInfo;
import com.jthinking.common.util.ip.IPInfoUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.noear.solon.annotation.*;
import org.noear.solon.core.handle.Context;
import org.noear.solon.validation.annotation.Validated;

import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 登陆控制器
 *
 * @author angis.cn
 * @Date 2023/1/6 18:56
 */
@Controller
public class LoginController extends BaseController<AngisUserService, AngisUser> {

    @Inject
    private DeptService deptService;
    @Inject
    private RoleresService roleresService;
    @Inject
    private RoleService roleService;
    @Inject
    private AngisUserService angisUserService;


    /**
     * 用户登陆
     * * @param loginInput
     *
     * @return: R<LoginOutput>
     * @throws:
     * @Date: 2023/1/7
     */
    @Post
    @SLog("登陆")
    @Mapping("login")
    public R<LoginOutput> login(@Validated LoginInput loginInput, Context ctx) {
        return loginVali(loginInput, DeviceType.PC.getCode(), ctx);
    }

    private R<LoginOutput> loginVali(LoginInput loginInput, String deviceType, Context ctx) {
        loginInput.setPassword(SM2.decrypt(loginInput.getPassword(), Crypto.PRIVATEKEY));

        AngisUser angisUser = baseService.getByUserName(loginInput.getUsername());
        if (angisUser == null) {
            saveLoginTimes(angisUser.getUsername());
            return R.error(ApiErrorCode.USER_PASSWORD_ERROR);
        }
        if (Constant.DISABLE.equals(angisUser.getStatus()) || StpUtil.isDisable(angisUser.getUsername())) {
            return R.error(ApiErrorCode.USER_LOCKED_ERROR + "，锁定剩余时间：" + StpUtil.getDisableTime(angisUser.getUsername()));
        }
//        if (logintype) {
            if (!angisUser.getPassword().equals(SM3.getDigest(loginInput.getPassword()))) {
                saveLoginTimes(angisUser.getUsername());
                return R.error(ApiErrorCode.USER_PASSWORD_ERROR);
            }
//        } else {
//            if (!angisUser.getPassword().equals(loginInput.getPassword())) {
//                saveLoginTimes(angisUser.getUsername());
//                return R.error(ApiErrorCode.USER_PASSWORD_ERROR);
//            }
//        }

        LoginOutput loginOutput = UserStruct.INSTANCE.toLoginOutPut(angisUser);
        // 设置权限
        loginOutput.setResources(roleresService.getRoleAndResourceByUserId(angisUser.getId()));
        if (StringUtils.isNotBlank(angisUser.getDeptId())) {
            loginOutput.setDeptName(deptService.getById(angisUser.getDeptId()).getName());
        }
        List<Role> roleList = roleService.getRoleListByUserId(angisUser.getId());
        if (CollectionUtils.isNotEmpty(roleList)) {
            loginOutput.setRoles(roleList);
            loginOutput.setRoleName(String.join(",", roleList.stream().map(Role::getName).collect(Collectors.toList())));
        }
        // sa-token 登录
        StpUtil.login(angisUser.getId(), deviceType);
        UserAgent ua = new UserAgent(ctx.userAgent());
        loginOutput.setIp(ctx.realIp());
        loginOutput.setOs(ua.getOperatingSystem().getName());
        loginOutput.setBrowser(ua.getBrowser().getName());
        IPInfo ipInfo = IPInfoUtils.getIpInfo(ctx.realIp());
        loginOutput.setLoginLocation(ipInfo.getAddress());
        loginOutput.setLastLoginTime(new Date());
        loginOutput.setExpireTime(new Date(System.currentTimeMillis() + StpUtil.getTokenTimeout()*1000));

        // 获取token
        SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
        // 获取session
        SaSession session = StpUtil.getSession();
        session.set(Constant.SESSION_LOGIN_NAME, loginOutput.getUsername());
        session.set(Constant.SESSION_USER_NAME, loginOutput.getNickName());
        session.set(Constant.SESSION_USER_ID, loginOutput.getId());
        session.set(Constant.SESSION_DEPT_ID, loginOutput.getDeptId());
        // 设置用户信息
        loginOutput.setToken(tokenInfo.getTokenValue());
        updateLoginUser(loginOutput);
        loginOutput.setLastLoginTime(null);
        loginOutput.setExpireTime(null);
        session.set(Constant.SESSION_USER_KEY, loginOutput);
        return success(loginOutput);
    }

    private void updateLoginUser(LoginOutput loginOutput) {
        AngisUser angisUser = UserStruct.INSTANCE.loginOutPutToUser(loginOutput);
        angisUserService.update(angisUser);
    }

    @Post
    @SLog("刷新token")
    @Mapping("refresh")
    public SaResult refresh(String refreshToken) {
        StpUtil.checkActiveTimeout();
        Object userId = SaTempUtil.parseToken(refreshToken);
        if (userId == null) {
            return SaResult.error("无效 Token");
        }
//        StpUtil.updateLastActiveToNow();
        String accessToken = StpUtil.createLoginSession(userId);
        StpUtil.logoutByTokenValue(refreshToken);
        return SaResult.data(accessToken);
    }

    private void saveLoginTimes(String userAccount) {
        StpUtil.checkDisable(userAccount);
        String loginErrorKey = Constant.SESSION_LOGIN_NAME + userAccount;
        String strnum = SaManager.getSaTokenDao().get(loginErrorKey);
        Integer number = strnum == null ? null : Integer.valueOf(strnum);
        if (number == null) {
            // 如果redis中没有保存，代表失败第一次
            number = 1;
        } else if (number < 5) {
            number++;
        }
        SaManager.getSaTokenDao().set(loginErrorKey, number.toString(), Constant.USER_LOCK_TIME);
        if (number < 5) return;
        // 第五次封禁账号,第六次进入isDisableTime方法，返回用户还需等待时间
        StpUtil.disable(userAccount, Constant.USER_LOCK_TIME);
        // 删除redis 中的key
        clearLoginErrorTimes(userAccount);
    }

    private void clearLoginErrorTimes(String userAccount) {
        String loginErrorKey = Constant.SESSION_LOGIN_NAME + userAccount;
        SaManager.getSaTokenDao().delete(loginErrorKey);
    }

    /**
     * 获取用户信息
     * * @param
     *
     * @return: R<LoginOutput>
     * @throws:
     * @Date: 2023/1/7
     */
    @Get
    @SLog
    @Mapping("getUser")
    public R<LoginOutput> getUser() {
        SaSession session = StpUtil.getSession();
        LoginOutput loginOutputDTO = session.getModel(Constant.SESSION_LOGIN_NAME, LoginOutput.class);
        return success(loginOutputDTO);
    }

    /**
     * 用户退出
     * * @param
     *
     * @return: R<String>
     * @throws:
     * @Date: 2023/1/7
     */
    @Post
    @SLog("退出")
    @Mapping("logout")
    public R<String> logout() {
        Thread.currentThread().setName(StpUtil.getSession().get(Constant.SESSION_LOGIN_NAME).toString());
        StpUtil.logout();
        return success("退出成功");
    }

}
